/**
 * Matter Types for Homebridge Plugin API
 *
 * This module provides types and interfaces for plugin developers
 * to create Matter-compatible accessories.
 */

import type { Endpoint, EndpointType } from '@matter/main'

// Import Matter.js clusters and device types
import * as clusters from '@matter/main/clusters'
import * as devices from '@matter/main/devices'

/**
 * Matter command handler function type
 *
 * Handlers can be synchronous or asynchronous (returning a Promise).
 * The args parameter contains the command arguments passed by Matter.js (optional).
 */
export type MatterCommandHandler<TArgs = unknown> = (args?: TArgs) => Promise<void> | void

/**
 * Matter cluster handlers interface
 *
 * Maps command names to their handler functions.
 * Each command can have custom argument types.
 */
export interface MatterClusterHandlers {
  [commandName: string]: MatterCommandHandler
}

/**
 * Matter Accessory - Plugin API Interface
 *
 * This is the main interface that plugin developers use to register
 * Matter accessories with Homebridge.
 */
export interface MatterAccessory<T = Record<string, any>> {
  /** Unique identifier for this accessory (must be unique across all accessories) */
  uuid: string

  /** Display name for the accessory */
  displayName: string

  /** Matter device type (e.g., OnOffLightDevice, DimmableLightDevice, etc.) */
  deviceType: EndpointType

  /** Serial number for the device */
  serialNumber: string

  /** Manufacturer name */
  manufacturer: string

  /** Model name/identifier */
  model: string

  /** Firmware revision (optional) */
  firmwareRevision?: string

  /** Hardware revision (optional) */
  hardwareRevision?: string

  /** Software version (optional) */
  softwareVersion?: string

  /**
   * Plugin developer storage - persists across restarts
   * This is a way for plugin developers to store custom data with their accessory
   * Similar to `PlatformAccessory.context` for HAP accessories
   */
  context?: T

  /**
   * Initial cluster states
   * Key is the cluster name, value is an object of attribute name -> value
   *
   * Example:
   * {
   *   onOff: { onOff: true },
   *   levelControl: { currentLevel: 127, minLevel: 1, maxLevel: 254 }
   * }
   */
  clusters: {
    [clusterName: string]: {
      [attributeName: string]: unknown
    }
  }

  /**
   * Handlers for Matter commands (Home app → Device)
   *
   * These handlers are called when a user controls the accessory via the Home app.
   * Use handlers to send commands to your actual device (cloud API, local network, etc.).
   */
  handlers?: {
    [clusterName: string]: MatterClusterHandlers
  }

  /**
   * Optional: Get current state handler
   * Called when a Matter controller reads an attribute
   * If not provided, the last set value is returned
   */
  getState?: (cluster: string, attribute: string) => Promise<any> | any
}

/**
 * Matter Configuration (for bridge or child bridge)
 */
export interface MatterConfig {
  /** Port for Matter server (optional, will auto-assign if not specified) */
  port?: number

  /** Name for the Matter bridge (optional) */
  name?: string
}

/**
 * Matter Fabric Information
 * Represents a commissioned controller (fabric) on the Matter bridge
 */
export interface MatterFabricInfo {
  fabricIndex: number
  fabricId: string
  nodeId: string
  rootVendorId: number
  label?: string
}

/**
 * Matter Server Events
 * Event types emitted by the Matter server for commissioning lifecycle
 */
export interface MatterServerEvents {
  /** Emitted when the bridge is commissioned for the first time */
  'commissioned': (fabricInfo: MatterFabricInfo) => void

  /** Emitted when the last fabric is removed (bridge becomes uncommissioned) */
  'decommissioned': () => void

  /** Emitted when a new fabric (controller) is added */
  'fabric-added': (fabricInfo: MatterFabricInfo) => void

  /** Emitted when a fabric (controller) is removed */
  'fabric-removed': (fabricInfo: MatterFabricInfo) => void

  /** Emitted whenever commissioning state changes (any fabric added/removed) */
  'commissioning-changed': (commissioned: boolean, fabricCount: number) => void

  /** Emitted when an accessory is registered */
  'accessory-registered': (accessory: MatterAccessory) => void

  /** Emitted when an accessory is unregistered */
  'accessory-unregistered': (uuid: string) => void
}

/**
 * Internal Matter accessory representation
 * (Used internally by MatterServer)
 */
export interface InternalMatterAccessory extends MatterAccessory {
  endpoint?: Endpoint
  registered: boolean
}

/**
 * Matter error type enum (for error handler categorization)
 */
export enum MatterErrorType {
  INITIALIZATION = 'INITIALIZATION',
  NETWORK = 'NETWORK',
  COMMISSIONING = 'COMMISSIONING',
  DEVICE_SYNC = 'DEVICE_SYNC',
  SERVER = 'SERVER',
  STORAGE = 'STORAGE',
  CONFIGURATION = 'CONFIGURATION',
  DEVICE_ERROR = 'DEVICE_ERROR',
  UNKNOWN = 'UNKNOWN',
}

/**
 * Matter error details interface
 */
export interface MatterErrorDetails {
  type?: MatterErrorType
  recoverable?: boolean
  code?: string
  context?: string
  originalError?: Error
}

/**
 * Matter error types
 */
export class MatterError extends Error {
  public readonly type: MatterErrorType
  public readonly timestamp: Date
  public readonly recoverable: boolean

  constructor(
    message: string,
    public readonly code: string,
    public readonly details?: MatterErrorDetails,
    type: MatterErrorType = MatterErrorType.UNKNOWN,
    recoverable = true,
  ) {
    super(message)
    this.name = 'MatterError'
    this.type = type
    this.timestamp = new Date()
    this.recoverable = recoverable
  }
}

export class MatterCommissioningError extends MatterError {
  constructor(message: string, details?: MatterErrorDetails) {
    super(message, 'COMMISSIONING_ERROR', details, MatterErrorType.COMMISSIONING)
    this.name = 'MatterCommissioningError'
  }
}

export class MatterStorageError extends MatterError {
  constructor(message: string, details?: MatterErrorDetails) {
    super(message, 'STORAGE_ERROR', details, MatterErrorType.STORAGE)
    this.name = 'MatterStorageError'
  }
}

export class MatterDeviceError extends MatterError {
  constructor(message: string, details?: MatterErrorDetails) {
    super(message, 'DEVICE_ERROR', details, MatterErrorType.DEVICE_ERROR)
    this.name = 'MatterDeviceError'
  }
}

export class MatterNetworkError extends MatterError {
  constructor(message: string, details?: MatterErrorDetails) {
    super(message, 'NETWORK_ERROR', details, MatterErrorType.NETWORK)
    this.name = 'MatterNetworkError'
  }
}

// Export Matter.js clusters for direct access
export { clusters }

// Export raw devices namespace (for advanced use)
export { devices }

/**
 * Friendly device type names for the Plugin API
 * Maps simplified names to actual Matter.js device types
 */
export const deviceTypes = {
  // Lighting
  OnOffLight: devices.OnOffLightDevice,
  DimmableLight: devices.DimmableLightDevice,
  ColorTemperatureLight: devices.ColorTemperatureLightDevice,
  ExtendedColorLight: devices.ExtendedColorLightDevice,

  // Switches & Outlets
  OnOffSwitch: devices.OnOffLightSwitchDevice,
  OnOffOutlet: devices.OnOffPlugInUnitDevice,
  DimmableOutlet: devices.DimmablePlugInUnitDevice,

  // Sensors
  TemperatureSensor: devices.TemperatureSensorDevice,
  HumiditySensor: devices.HumiditySensorDevice,
  LightSensor: devices.LightSensorDevice,
  MotionSensor: devices.OccupancySensorDevice,
  ContactSensor: devices.ContactSensorDevice,
  LeakSensor: devices.WaterLeakDetectorDevice,
  SmokeSensor: devices.SmokeCoAlarmDevice,

  // HVAC
  Thermostat: devices.ThermostatDevice,
  Fan: devices.FanDevice,

  // Security
  DoorLock: devices.DoorLockDevice,

  // Window Coverings
  WindowCovering: devices.WindowCoveringDevice,

  // Other
  GenericSwitch: devices.GenericSwitchDevice,
  Pump: devices.PumpDevice,
  RoomAirConditioner: devices.RoomAirConditionerDevice,
} as const
